#include "Face.h"
#include "ui_Face.h"
#include "Server.h"
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QProcess>
#include <QDebug>
#include <QSqlQuery>

#include <iostream>
#include "face.hpp"

#include<opencv2\core.hpp>
#include<opencv2\highgui.hpp>
#include<opencv2\imgproc.hpp>
#include <math.h>
#include <fstream>
#include <sstream>

#include <QFile>
#include <QTextStream>
#include <QSqlQuery>

#include <QBitmap>
#include <QStyleOption>
#include <QMouseEvent>
#include <QPainter>

#include "mymessagebox.h"

using namespace std;
using namespace cv;

Face::Face(Server *server, QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Face)
    , serverWindow(server)
    , listModel(new QStandardItemModel(this))
{
    ui->setupUi(this);

    // 隐藏原生标题栏
    setWindowFlags(Qt::FramelessWindowHint | Qt::Dialog);

    // 防止窗口背景透明
    setAttribute(Qt::WA_TranslucentBackground, false);

    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("attendance_system.db");

    if (!db.open()) {
        // QMessageBox::critical(this, "错误", "无法打开数据库: " + db.lastError().text());
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("无法打开数据库: " + db.lastError().text());
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // 设置 QListView 的模型
    ui->list->setModel(listModel);

    // 连接列表项点击信号到槽函数
    connect(ui->list, &QListView::clicked, this, &Face::on_list_clicked);

    // 加载 FaceVerification 数据
    loadFaceVerificationData();
}

Face::~Face()
{
    delete ui;
}

// 实现窗口拖动功能
void Face::mousePressEvent(QMouseEvent *event)
{
    // 检查是否点击了自定义标题栏
    if (event->button() == Qt::LeftButton && event->pos().y() <= 60) {
        m_isTitleBarClicked = true;
        m_dragPosition = event->globalPos() - frameGeometry().topLeft();
        event->accept();
    } else {
        m_isTitleBarClicked = false;
        // 让事件继续传播，确保其他控件能响应点击事件
        QWidget::mousePressEvent(event);
    }
}

void Face::mouseMoveEvent(QMouseEvent *event)
{
    // 只有在点击了标题栏之后才允许拖动
    if (m_isTitleBarClicked && (event->buttons() & Qt::LeftButton)) {
        move(event->globalPos() - m_dragPosition);
        event->accept();
    } else {
        // 让事件继续传播，确保其他控件能响应移动事件
        QWidget::mouseMoveEvent(event);
    }
}

void Face::paintEvent(QPaintEvent *event)
{
    // 1. 绘制样式（QSS）
    QStyleOption opt;
    opt.initFrom(this);
    QPainter p(this);
    style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); // 绘制样式

    // 2. 设置掩码，约定 painter 绘制区域
    QBitmap bmp(this->size());
    bmp.fill(Qt::white); // 填充 bitmap

    QPainter painter(&bmp);
    painter.setPen(Qt::NoPen);
    painter.setBrush(Qt::black); // 掩码值
    painter.drawRoundedRect(bmp.rect(), 30, 30); // 在 bitmap 中绘制圆角矩形

    // 设置掩码以确定本控件可绘制区域
    setMask(bmp);

    // 调用基类的 paintEvent
    QWidget::paintEvent(event);
}

static Mat norm_0_255(InputArray _src) {
    Mat src = _src.getMat();
    // 创建和返回一个归一化后的图像矩阵:
    Mat dst;
    switch (src.channels()) {
    case 1:
        cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC1);
        break;
    case 3:
        cv::normalize(_src, dst, 0, 255, NORM_MINMAX, CV_8UC3);
        break;
    default:
        src.copyTo(dst);
        break;
    }
    return dst;
}


//使用CSV文件去读图像和标签，主要使用stringstream和getline方法
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
    std::ifstream file(filename.c_str(), ifstream::in);//c_str()函数可用可不用，无需返回一个标准C类型的字符串
    if (!file)
    {
        string error_message = "No valid input file was given, please check the given filename.";
        CV_Error(cv::Error::StsBadArg, error_message);
    }
    string line, path, classlabel;
    while (getline(file, line)) //从文本文件中读取一行字符，未指定限定符默认限定符为“/n”
    {
        stringstream liness(line);//这里采用stringstream主要作用是做字符串的分割
        getline(liness, path, separator);//读入图片文件路径以分好作为限定符
        getline(liness, classlabel);//读入图片标签，默认限定符
        if (!path.empty() && !classlabel.empty()) //如果读取成功，则将图片和对应标签压入对应容器中
        {
            images.push_back(imread(path, 0));
            labels.push_back(atoi(classlabel.c_str()));
        }
    }
}

void Face::loadFaceVerificationData()
{
    QSqlQuery query;
    query.prepare("SELECT 工号, 姓名, 申请时间 FROM FaceVerification WHERE 申请状态 = 0");

    if (query.exec()) {
        while (query.next()) {
            QString employeeId = query.value(0).toString();
            QString name = query.value(1).toString();
            QString applicationTime = query.value(2).toString();

            QString displayText = employeeId + "-" + name + "-" + applicationTime;

            // 创建一个项并将其添加到模型中
            QStandardItem *item = new QStandardItem(displayText);
            listModel->appendRow(item);
        }
    } else {
        qDebug() << "Failed to query FaceVerification: " << query.lastError();
    }
}

void Face::on_list_clicked(const QModelIndex &index)
{
    // 获取选中项的文本
    QString selectedText = listModel->itemFromIndex(index)->text();

    // 从文本中提取工号 (文本格式为 "工号-姓名-申请时间")
    QString employeeId = selectedText.split("-").first();

    // 构建工号文件夹的路径
    QString folderPath = QDir::currentPath() + "/" + employeeId;
    QString imagePath = folderPath + "/1.jpg";

    // 检查图像文件是否存在
    if (QFile::exists(imagePath)) {
        QPixmap pixmap(imagePath);
        ui->face->setPixmap(pixmap.scaled(ui->face->size(), Qt::KeepAspectRatio));
    } else {
        qDebug() << "Image not found: " << imagePath;
        ui->face->clear();  // 清空 QLabel
    }
}

void Face::on_returnButton_clicked()
{
    this->close();
    serverWindow->show();
}


void Face::on_train_clicked()
{
    // 运行人脸识别模型训练代码
    //读取你的CSV文件路径.
    //string fn_csv = string(argv[1]);
    string fn_csv = "D:\\Qt_project\\Server\\at.txt";

    // 2个容器来存放图像数据和对应的标签
    vector<Mat> images;
    vector<int> labels;
    // 读取数据. 如果文件不合法就会出错
    // 输入的文件名已经有了.
    try
    {
        read_csv(fn_csv, images, labels); //从csv文件中批量读取训练数据
    }
    catch (cv::Exception& e)
    {
        cerr << "Error opening file \"" << fn_csv << "\". Reason: " << e.msg << endl;
        // 文件有问题，我们啥也做不了了，退出了
        exit(1);
    }
    // 如果没有读取到足够图片，也退出.
    if (images.size() <= 1) {
        string error_message = "This demo needs at least 2 images to work. Please add more images to your data set!";
        CV_Error(cv::Error::StsBadArg, error_message);
    }

    for (int i = 0; i < images.size(); i++)
    {
        //cout<<images.size();
        if (images[i].size() != Size(92, 112))
        {
            cout << i << endl;
            cout << images[i].size() << endl;
        }

    }

    // 下面的几行代码仅仅是从你的数据集中移除最后一张图片，作为测试图片
    //[gm:自然这里需要根据自己的需要修改，他这里简化了很多问题]
    Mat testSample = images[images.size() - 1];
    int testLabel = labels[labels.size() - 1];
    images.pop_back();//删除最后一张照片，此照片作为测试图片
    labels.pop_back();//删除最有一张照片的labels
    // 下面几行创建了一个特征脸模型用于人脸识别，
    // 通过CSV文件读取的图像和标签训练它。
    // T这里是一个完整的PCA变换
    //如果你只想保留10个主成分，使用如下代码
    //      cv::createEigenFaceRecognizer(10);
    //
    // 如果你还希望使用置信度阈值来初始化，使用以下语句：
    //      cv::createEigenFaceRecognizer(10, 123.0);
    //
    // 如果你使用所有特征并且使用一个阈值，使用以下语句：
    //      cv::createEigenFaceRecognizer(0, 123.0);

    //创建一个PCA人脸分类器，暂时命名为model吧，创建完成后
    //调用其中的成员函数train()来完成分类器的训练
    Ptr<face::BasicFaceRecognizer> model = face::EigenFaceRecognizer::create();
    model->train(images, labels);
    model->save("MyFacePCAModel.xml");//保存路径可自己设置，但注意用“\\”

    Ptr<face::BasicFaceRecognizer> model1 = face::FisherFaceRecognizer::create();
    model1->train(images, labels);
    model1->save("MyFaceFisherModel.xml");

    Ptr<face::LBPHFaceRecognizer> model2 = face::LBPHFaceRecognizer::create();
    model2->train(images, labels);
    model2->save("MyFaceLBPHModel.xml");

    // 下面对测试图像进行预测，predictedLabel是预测标签结果
    //注意predict()入口参数必须为单通道灰度图像，如果图像类型不符，需要先进行转换
    //predict()函数返回一个整形变量作为识别标签
    int predictedLabel = model->predict(testSample);//加载分类器
    int predictedLabel1 = model1->predict(testSample);
    int predictedLabel2 = model2->predict(testSample);

    // 还有一种调用方式，可以获取结果同时得到阈值:
    // int predictedLabel = -1;
    // double confidence = 0.0;
    //  model->predict(testSample, predictedLabel, confidence);

    string result_message = format("Predicted class = %d / Actual class = %d.", predictedLabel, testLabel);
    string result_message1 = format("Predicted class = %d / Actual class = %d.", predictedLabel1, testLabel);
    string result_message2 = format("Predicted class = %d / Actual class = %d.", predictedLabel2, testLabel);
    cout << result_message << endl;
    cout << result_message1 << endl;
    cout << result_message2 << endl;

    // QMessageBox::information(this, "提示", "训练完成");
    MyMessageBox msgBox;
    msgBox.setWindowTitle("提示");
    msgBox.setText("训练完成");
    msgBox.setButton("确定");
    msgBox.exec();

    getchar();
    //waitKey(0);
    return;
}


void Face::on_push_clicked()
{
    // 定义源文件路径（当前目录）
    QString sourceFile = QDir::currentPath() + "/MyFaceFisherModel.xml";

    // 定义目标目录路径
    QString destinationDir = "D:/Qt_project/FaceRecognition/build/Desktop_Qt_5_15_2_MinGW_64_bit-Debug/";

    // 定义目标文件路径
    QString destinationFile = destinationDir + "MyFaceFisherModel.xml";

    // 检查源文件是否存在
    if (QFile::exists(sourceFile)) {

        // 如果目标目录下已经存在同名文件，则删除它
        if (QFile::exists(destinationFile)) {
            QFile::remove(destinationFile);  // 删除旧文件
        }

        // 复制文件
        if (QFile::copy(sourceFile, destinationFile)) {
            // 文件复制成功，弹出提示框
            // QMessageBox::information(this, "提示", "文件复制并覆盖成功！");
            MyMessageBox msgBox;
            msgBox.setWindowTitle("提示");
            msgBox.setText("文件复制并覆盖成功！");
            msgBox.setButton("确定");
            msgBox.exec();
        } else {
            // 文件复制失败，弹出错误提示框
            // QMessageBox::warning(this, "错误", "文件复制失败！");
            MyMessageBox msgBox;
            msgBox.setWindowTitle("错误");
            msgBox.setText("文件复制失败！");
            msgBox.setButton("确定");
            msgBox.exec();
        }
    } else {
        // 源文件不存在，弹出错误提示框
        // QMessageBox::warning(this, "错误", "源文件不存在！");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("源文件不存在！");
        msgBox.setButton("确定");
        msgBox.exec();
    }
}


void Face::on_yes_clicked()
{
    // 确保已经选择了申请记录
    if (ui->list->currentIndex().row() == -1) {
        // QMessageBox::warning(this, "警告", "请选择申请记录");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("警告");
        msgBox.setText("请选择申请记录");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // 获取当前选中的申请记录
    QString selectedText = ui->list->currentIndex().data().toString();
    QString employeeID = selectedText.split('-')[0];  // 获取工号

    // 查询数据库，更新FaceVerification表的申请状态为1（审核通过）
    QSqlQuery query;
    query.prepare("UPDATE FaceVerification SET 申请状态 = 1 WHERE 工号 = :employeeID");
    query.bindValue(":employeeID", employeeID);
    if (!query.exec()) {
        // QMessageBox::warning(this, "错误", "数据库操作失败！");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("数据库操作失败！");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // 查找当前目录下名为工号的文件夹，将其中的所有文件复制到D:/Qt_project/Server/orl_faces下的新文件夹
    QString sourceDir = QDir::currentPath() + "/" + employeeID;
    QDir destinationDir("D:/Qt_project/Server/orl_faces");
    QStringList subDirs = destinationDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    int newFolderIndex = subDirs.size() + 1;
    QString newFolder = destinationDir.absolutePath() + QString("/s%1").arg(newFolderIndex);

    // 创建新文件夹
    if (!QDir().mkdir(newFolder)) {
        // QMessageBox::warning(this, "错误", "创建目标文件夹失败！");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("创建目标文件夹失败！");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // 复制文件
    QDir dir(sourceDir);
    QStringList files = dir.entryList(QDir::Files);
    foreach (QString fileName, files) {
        QFile::copy(sourceDir + "/" + fileName, newFolder + "/" + fileName);
    }

    // 运行Python脚本 create_csv.py
    QProcess::execute("python D:/Qt_project/Server/create_csv.py");

    // 读取D:/Qt_project/Server/at.txt文件
    QFile file("D:/Qt_project/Server/at.txt");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        // QMessageBox::warning(this, "错误", "无法打开文件at.txt！");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("无法打开文件at.txt！");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    QTextStream in(&file);
    QString line;
    while (!in.atEnd()) {
        line = in.readLine();
        // 解析每一行，找到分号后的数字
        QStringList parts = line.split(';');
        if (parts.size() == 2) {
            QString imagePath = parts[0];  // 图像路径
            int label = parts[1].toInt();  // 标签号（分号后面的数字）

            // 查找int newFolderIndex = subDirs.size() + 1;获取到的工号路径
            if (imagePath.contains(QString("D:/Qt_project/Server/orl_faces\\s%1/1").arg(newFolderIndex))) {
                // 使用employeeID查询数据库并更新标签号
                QSqlQuery query;
                query.prepare("UPDATE EmployeeInfo SET 标签号 = :label WHERE 工号 = :employeeID");
                query.bindValue(":label", label);
                query.bindValue(":employeeID", employeeID);
                if (!query.exec()) {
                    // QMessageBox::warning(this, "错误", "数据库更新标签号失败！");
                    MyMessageBox msgBox;
                    msgBox.setWindowTitle("错误");
                    msgBox.setText("数据库更新标签号失败！");
                    msgBox.setButton("确定");
                    msgBox.exec();
                    return;
                }
            }
        }
    }

    file.close();

    // QMessageBox::information(this, "提示", "审核通过！");
    MyMessageBox msgBox;
    msgBox.setWindowTitle("提示");
    msgBox.setText("审核通过！");
    msgBox.setButton("确定");
    msgBox.exec();

    listModel->clear();
    ui->list->setModel(listModel);

    // 清空 QLabel 中的图片
    ui->face->clear();

    // 加载 FaceVerification 数据
    loadFaceVerificationData();
    return;

}


void Face::on_no_clicked()
{
    // 确保已经选择了申请记录
    if (ui->list->currentIndex().row() == -1) {
        // QMessageBox::warning(this, "警告", "请选择申请记录");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("警告");
        msgBox.setText("请选择申请记录");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // 获取当前选中的申请记录
    QString selectedText = ui->list->currentIndex().data().toString();
    QString employeeID = selectedText.split('-')[0];  // 获取工号

    // 查询数据库，更新FaceVerification表的申请状态为2（审核未通过）
    QSqlQuery query;
    query.prepare("UPDATE FaceVerification SET 申请状态 = 2 WHERE 工号 = :employeeID");
    query.bindValue(":employeeID", employeeID);
    if (!query.exec()) {
        // QMessageBox::warning(this, "错误", "数据库操作失败！");
        MyMessageBox msgBox;
        msgBox.setWindowTitle("错误");
        msgBox.setText("数据库操作失败！");
        msgBox.setButton("确定");
        msgBox.exec();
        return;
    }

    // QMessageBox::information(this, "提示", "审核未通过！");
    MyMessageBox msgBox;
    msgBox.setWindowTitle("提示");
    msgBox.setText("审核未通过！");
    msgBox.setButton("确定");
    msgBox.exec();

    // 加载 FaceVerification 数据
    loadFaceVerificationData();

}
